package cn.jbolt.ai.app.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.jbolt.ai.app.AppPublishType;
import cn.jbolt.ai.app.entity.AiApp;
import cn.jbolt.ai.app.entity.AiAppNode;
import cn.jbolt.ai.app.service.AiAppNodeService;
import cn.jbolt.ai.app.vo.*;
import cn.jbolt.ai.core.chain.ChainCenter;
import cn.jbolt.ai.core.chain.node.base.AppConfigConstant;
import cn.jbolt.ai.app.service.AiAppService;
import cn.jbolt.ai.core.chain.node.action.klb.KlbChatNodeConfig;
import cn.jbolt.ai.core.chain.node.InnerNodeType;
import cn.jbolt.ai.core.chain.node.base.UiAppConfig;
import cn.jbolt.util.JSONUtil;
import cn.jbolt.util.cache.CacheUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.jbolt.ai.app.dao.AiAppConfigMapper;
import cn.jbolt.ai.app.entity.AiAppConfig;
import cn.jbolt.ai.app.service.AiAppConfigService;
import cn.jbolt.util.Result;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class AiAppConfigServiceImpl extends ServiceImpl<AiAppConfigMapper, AiAppConfig> implements AiAppConfigService {

    private static final Logger logger = LoggerFactory.getLogger(AiAppConfigServiceImpl.class);
    @Autowired
    private AiAppService appService;
    @Autowired
    protected AiAppNodeService nodeService;
    @Autowired
    private ChainCenter chainCenter;


//    @Override
//    public Result add(String linkType, Long linkId, String configKey, JsonNode configValue) {
//        AiAppConfig configDb = new AiAppConfig();
//        configDb.setLinkId(linkId);
//        configDb.setLinkType(linkType);
//        configDb.setConfigValue(configValue.toString());
//        boolean save = this.save(configDb);
//        if (!save) {
//            return Result.error("添加失败");
//        }
//        //移除之前的缓存
//        CacheUtil.removeObject(CacheUtil.KEY_APP_CONFIG + "_" + linkType + "_" + linkId);
//        //查询所有的缓存,存入
//        getConfigAndUpdateCache(linkType, linkId);
//        return Result.success();
//    }


    @Transactional
    public Result add(String linkType, Long linkId, JsonNode config) {

        AiAppConfig configDb = new AiAppConfig();
        configDb.setLinkId(linkId);
        configDb.setLinkType(linkType);
        configDb.setConfigValue(JSONUtil.toJson(config));
        boolean save = this.save(configDb);
        if (!save) {
            return Result.error("添加失败");
        }
        //移除之前的缓存
        CacheUtil.removeObject(CacheUtil.KEY_APP_CONFIG + "_" + linkType + "_" + linkId);
        //获取新的配置,在set到缓存中
        getConfig(linkType, linkId);
        return Result.success();
    }

    @Override
    public Result update(String linkType, Long linkId, String configValue) {
        AiAppConfig config = this.getOne(new QueryWrapper<AiAppConfig>().eq("link_id", linkId).
                eq("link_type", linkType));
        if (config != null) {
            config.setConfigValue(configValue);
            this.updateById(config);

        } else { //如果是null的话,就新建补上配置
            config = new AiAppConfig();
            config.setLinkId(linkId);
            config.setLinkType(linkType);
            config.setConfigValue(configValue);
            this.save(config);
        }
        //添加到缓存
        removeCache(linkType, linkId);
        return Result.success(JSONUtil.toJsonObject(configValue));

    }


    @Override
    @Transactional
    public Result updateConfigByKey(String linkType, Long linkId, String configKey, String configValue) {
        AiAppConfig config = this.getOne(new QueryWrapper<AiAppConfig>().eq("link_id", linkId).
                eq("link_type", linkType));

        ObjectNode newConfig = null;

        if (config != null) {
            //先拿到全部的配置

            ObjectNode dbConfig = JSONUtil.toJsonObject(config.getConfigValue());

            newConfig = (ObjectNode) JSONUtil.merge(dbConfig,
                    JSONUtil.createObjectNode(configKey, JSONUtil.toJsonObject(configValue)));
//            if (Objects.equals(linkType, AppConfigConstant.LINK_TYPE_APP) && Objects.equals(configKey, AppConfigConstant.CONFIG_TYPE_KLB_CHAT)) {
//                //判断configKey是否是klbChatConfig
//                Result result  = checkNeedProcessSimpleAppNodes(linkId,
//                        dbConfig,
//                        newConfig
//                );
//                if (result.isError()) {
//                    return result;
//                }
//            }

            config.setConfigValue(JSONUtil.toJson(newConfig));

            updateById(config);


        } else { //如果是null的话,就新建补上配置
            config = new AiAppConfig();
            config.setLinkId(linkId);
            config.setLinkType(linkType);
            //把key-value转成json存出
            newConfig = JSONUtil.createObjectNode();
            newConfig.put(configKey, configValue);
            config.setConfigValue(JSONUtil.toJson(newConfig));
            this.save(config);
        }
        //添加到缓存
        removeCache(linkType, linkId);

        return Result.success(newConfig);

    }

    /**
     * 1.判断是否是简单应用
     * 2.判断要修改的config是否是klbChatConfig
     * 3.判断klbChatConfig中的是否仅知识库模式.未匹配到知识库的回答这两个是否更改
     * 4.如果没有更改,则不需要更新node
     * 5.如果有更改,则需要更新node上的没有匹配到的数据
     *
     * @param appId
     * @param oldConfigJson
     * @param newConfigJson
     * @return
     */

    private Result checkNeedProcessSimpleAppNodes(Long appId, ObjectNode oldConfigJson, ObjectNode newConfigJson) {

        KlbChatNodeConfig oldKlbConfig = KlbChatNodeConfig.from(oldConfigJson.get(AppConfigConstant.CONFIG_TYPE_KLB_CHAT));
        KlbChatNodeConfig newKlbConfig = KlbChatNodeConfig.from(newConfigJson.get(AppConfigConstant.CONFIG_TYPE_KLB_CHAT));

        //判断是否仅知识库变了
        if (oldKlbConfig.isOnlyKlb() != newKlbConfig.isOnlyKlb()) {
            if (newKlbConfig.isOnlyKlb()) {//是仅知识库模式,要把chat变更成outputConfig
                Result<AiAppNode> result = nodeService.getNodeByAppIdAndType(appId, InnerNodeType.AI_CHAT);
                if (result.isError()) {
                    return result;
                }
                result.getData().setType(InnerNodeType.OUTPUT_TO_CLIENT);
                nodeService.update(result.getData());

            } else {//不是仅知识库模式,要把outputConfig变更成chat
                Result<AiAppNode> result = nodeService.getNodeByAppIdAndType(appId, InnerNodeType.OUTPUT_TO_CLIENT);
                if (result.isError()) {
                    return result;
                }
                result.getData().setType(InnerNodeType.AI_CHAT);
                nodeService.update(result.getData());
            }
            ChainCenter.remove(appId);
        }

        //提示语变了
        if (Objects.equals(oldKlbConfig.getNoMatchReply(), newKlbConfig.getNoMatchReply()) == false) {
            ObjectNode outputConfig = (ObjectNode) newConfigJson.get(AppConfigConstant.CONFIG_TYPE_OUTPUT_TO_CLIENT);
            outputConfig.put("inputTpl", newKlbConfig.getNoMatchReply());

        }

        return Result.success();
    }


    @Override
    @Transactional
    public Result updateConfigByBatch(AiConfigVO vo) {
        for (AiAppConfig config : vo.getConfigList()) {
            Result result = updateConfigByKey(vo.getLinkType(), vo.getLinkId(), config.getConfigKey(), config.getConfigValue());
            if (result.isError()) {
                return result;
            }
        }
        return Result.success();
    }

    @Override
    public Result delete(Long id) {
        boolean deleted = this.removeById(id);
        if (deleted) {
            return Result.success();
        } else {
            return Result.error("删除失败");
        }
    }

    /**
     * 获取某个节点最终生效的配置
     *
     * @param appId
     * @param sessionId
     * @param nodeId
     * @return
     */
    @Override
    public JsonNode getNodeValidConfig(Long appId, Long sessionId, Long nodeId) {
        ObjectNode validConfig = JSONUtil.createObjectNode();
        //先取出app的配置
        JsonNode config = getConfig(AppConfigConstant.LINK_TYPE_APP, appId);
        if (config != null) {
            JSONUtil.merge(validConfig, config);
        }
        //再取出node的配置,与app的合并
        config = getConfig(AppConfigConstant.LINK_TYPE_NODE, nodeId);
        if (config != null) {
            JSONUtil.merge(validConfig, config);
        }

        //最后取出session的配置
        config = getConfig(AppConfigConstant.LINK_TYPE_SESSION, sessionId);
        if (config != null) {
            JSONUtil.merge(validConfig, config);
        }

        return validConfig;
    }

    @Override
    public Result deleteByLinkId(String linkType, Long linkId) {
        QueryWrapper<AiAppConfig> query = new QueryWrapper<>();
        query.eq("link_type", linkType);
        query.eq("link_id", linkId);
        if (this.list(query).size() > 0) {
            boolean deleted = this.remove(query);
            if (deleted) {
                //移除缓存
                removeCache(linkType, linkId);
                return Result.success();
            }
            return Result.error("删除失败");
        }
        return Result.success();

    }

    @Override
    public Result deleteByAppId(Long appId) {
        QueryWrapper<AiAppConfig> query = new QueryWrapper<>();
        query.eq("link_id", appId);
        List<AiAppConfig> list = this.list(query);
        if (list.size() > 0) {
            boolean deleted = this.remove(query);
            if (deleted) {
                list.forEach(item -> {
                    removeCache(item.getLinkType(), item.getLinkId());
                });
                //直接清空app的缓存
                CacheUtil.removeObject(CacheUtil.KEY_APP + "_" + appId);
                logger.info("缓存中app移除成功:{}_{}", CacheUtil.KEY_APP, appId);
                return Result.success();
            }
            return Result.error("删除失败");
        }
        return Result.success();
    }

    @Override
    public Result deleteByNodeId(List<Long> nodeIds) {
        QueryWrapper<AiAppConfig> query = new QueryWrapper<>();
        query.eq("link_type", AppConfigConstant.LINK_TYPE_NODE);
        query.in("link_id", nodeIds);
        if (this.list(query).size() > 0) {
            boolean deleted = this.remove(query);
            if (deleted) {
                //直接清空app的缓存
                for (Long id : nodeIds) {
                    removeCache(AppConfigConstant.LINK_TYPE_NODE, id);
                }
                return Result.success();
            }
            return Result.error("删除失败");
        }
        return Result.success();
    }

    @Override
    public Result deleteByLinkType(String linkType, List<Long> linkId) {
        if (linkId != null && linkId.size() > 0) {
            QueryWrapper<AiAppConfig> query = new QueryWrapper<>();
            query.eq("link_type", linkType);
            query.in("link_id", linkId);
            List<AiAppConfig> list = this.list(query);
            if (list != null && list.size() > 0) {
                for (AiAppConfig item : list) {
                    removeCache(item.getLinkType(), item.getLinkId());
                }
                boolean deleted = this.remove(query);
                if (!deleted) {
                    return Result.error("删除失败");
                }
            }
        }
        return Result.success();
    }

    private void removeCache(String linkType, Long linkId) {
        CacheUtil.removeObject(CacheUtil.KEY_APP_CONFIG + "_" + linkType + "_" + linkId);
        logger.info("缓存中移除appConfig:{}_{}_{}", CacheUtil.KEY_APP_CONFIG, linkType, linkId);
    }

    @Override
    public JsonNode getConfig(String linkType, Long linkId) {
        JsonNode cache = CacheUtil.getObject(CacheUtil.KEY_APP_CONFIG + "_" + linkType + "_" + linkId);
        if (cache != null) {
            logger.info("缓存中取出appConfig:{}_{}_{}-----{}:", CacheUtil.KEY_APP_CONFIG, linkType, linkId, cache);
            return cache;
        }
        QueryWrapper<AiAppConfig> query = new QueryWrapper<>();
        query.eq("link_id", linkId);
        query.eq("link_type", linkType);
        AiAppConfig appConfig = this.getOne(query);
        if (appConfig != null) {
            ObjectNode objectNode = JSONUtil.toJsonObject(appConfig.getConfigValue());
            if (objectNode != null) {
                setCache(linkType, linkId, objectNode);
                return objectNode;
            }
        }
        return null;
    }

    @Override
    public Result getConfigByCondition(String linkType, Long linkId, String configKey) {
        JsonNode allConfig = getConfig(linkType, linkId);
        if (allConfig != null && allConfig.size() > 0) {
            JsonNode config = allConfig.get(configKey);
            if (config != null) {
                return Result.success(config);
            }
        }
        return Result.success(null);
    }

    @Override
    public Result getConfigListByCondition(String linkType, Long linkId, List<String> configKey) {
        ObjectNode objectNode = JSONUtil.createObjectNode();
        JsonNode allConfig = getConfig(linkType, linkId);
        if (allConfig != null && allConfig.size() > 0) {
            for (String key : configKey) {
                JsonNode config = allConfig.get(key);
                if (config != null) {
                    objectNode.put(key, config);
                }
            }
            return Result.success(objectNode);
        }
        return Result.success(null);
    }

    @Override
    public JsonNode getObjectConfigByCondition(String linkType, Long linkId, String configKey) {
        QueryWrapper<AiAppConfig> query = new QueryWrapper<>();
        query.eq("link_type", linkType);
        query.eq("link_id", linkId);
        AiAppConfig appConfig = this.getOne(query);
        if (appConfig != null) {
            String configValue = appConfig.getConfigValue();
            if (StrUtil.isNotBlank(configValue)) {
                ObjectNode jsonObject = JSONUtil.toJsonObject(configValue);
                JsonNode jsonNode = jsonObject.get(configKey);
                return jsonNode;

            }
        }

        return null;
    }

    @Override
    public Result delFuncConfigByFuncId(Long id) {
        //todo 待完善
        return Result.success();

    }

    @Override
    public Result delConfigByKlbId(Long klbId) {
        //todo 待完善
        return Result.success();

    }

    @Override
    public Result addConfigs(List<AiAppConfig> nodeConfigList) {
        boolean b = saveBatch(nodeConfigList);
        if (b) {
            for (AiAppConfig config : nodeConfigList) {
                if (config.getConfigValue() != null) {
                    setCache(config.getLinkType(), config.getLinkId(), JSONUtil.toJsonObject(config.getConfigValue()));
                }
            }
            return Result.success();
        }
        return Result.error("添加失败");
    }

    /**
     * 只修改应用的发布中的相关配置accessConfig
     *
     * @param accessConfigVO
     * @return
     */
    @Override
    @Transactional
    public Result updateAccessConfig(AccessConfigVO accessConfigVO) {
        Result<AiApp> appResult = appService.getAppById(accessConfigVO.getAppId());
        if (appResult.isError()) {
            return Result.error(appResult.getMsg());
        }
        //获取相关的配置
        QueryWrapper<AiAppConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("link_type", AppConfigConstant.LINK_TYPE_APP);
        queryWrapper.eq("link_id", accessConfigVO.getAppId());
        AiAppConfig appConfig = this.getOne(queryWrapper, false);
        if (appConfig == null) {
            appConfig = new AiAppConfig();
            appConfig.setLinkType(AppConfigConstant.LINK_TYPE_APP);
            appConfig.setLinkId(accessConfigVO.getAppId());
            ObjectNode configs = JSONUtil.createObjectNode();
            //默认给他set uiConfig
            configs.put(AppConfigConstant.CONFIG_TYPE_UI, UiAppConfig.getDefaultConfig());
            appConfig.setConfigValue(JSONUtil.toJson(configs));
            boolean save = save(appConfig);
            if (!save) {
                return Result.error("数据异常");
            }
        }
        //先找到原本的accessConfigOldObjectNode
        String configValue = appConfig.getConfigValue();
        ObjectNode jsonObject = JSONUtil.toJsonObject(configValue);
        JsonNode accessConfigJsonNode = jsonObject.get(AccessConfigConstant.ACCESS);
        //数据库中旧的accessConfig对象
        ObjectNode accessConfigOldObjectNode = JSONUtil.createObjectNode();
        if (accessConfigJsonNode != null) {
            accessConfigOldObjectNode = JSONUtil.toJsonObject(accessConfigJsonNode);
        }
        AccessConfig accessConfigDB = new AccessConfig();
        //判断本次修改的什么
        if (accessConfigVO.getAppPublishType().equals(AppPublishType.WEB.getCode())) { //如果是web
            accessConfigDB.setWeb(JSONUtil.toObject(accessConfigVO.getJsonConfig(), AccessWebConfig.class));
        }
        if (accessConfigVO.getAppPublishType().equals(AppPublishType.API.getCode())) {//如果是api
            accessConfigDB.setApi(JSONUtil.toObject(accessConfigVO.getJsonConfig(), AccessApiConfig.class));
        }
        ObjectNode accessConfigNewJsonObject = JSONUtil.toJsonObject(accessConfigDB);
        //最终的accessConfig对象
        JsonNode merge = JSONUtil.merge(accessConfigOldObjectNode, accessConfigNewJsonObject);
        //放到找到的config全部对象中
        jsonObject.put(AccessConfigConstant.ACCESS, merge);
        appConfig.setConfigValue(JSONUtil.toJson(jsonObject));
        if (this.updateById(appConfig)) {
            setCache(AppConfigConstant.LINK_TYPE_APP, appConfig.getLinkId(), jsonObject);
            return Result.success();
        }
        return Result.error("修改失败");

    }

    /**
     * 只获取应用的发布中的相关配置
     *
     * @param appId
     * @return
     */
    @Override
    public Result<AccessConfigVO> getAccessConfigByAppId(Long appId) {
        Result<AiApp> appResult = appService.getAppById(appId);
        if (appResult.isError()) {
            return Result.error(appResult.getMsg());
        }
        AiApp app = appResult.getData();
        //创建AccessConfig对象
        AccessConfigVO accessConfigVO = new AccessConfigVO();
        accessConfigVO.setAppId(app.getId());
        //获取相关的配置
        JsonNode jsonNode = app.getConfig();
        app.setConfig(null);//不要带太多的配置返回
        accessConfigVO.setApp(app);
        if (jsonNode != null) {
            ObjectNode jsonObject = JSONUtil.toJsonObject(jsonNode);
            JsonNode accessConfigJsonNode = jsonObject.get(AccessConfigConstant.ACCESS);
            if (accessConfigJsonNode != null) {
                //分别取出web,api
                if (accessConfigJsonNode.get(AccessConfigConstant.CONFIG_TYPE_WEB) != null) {
                    AccessWebConfig accessWebConfig = JSONUtil.toObject(accessConfigJsonNode.get(AccessConfigConstant.CONFIG_TYPE_WEB), AccessWebConfig.class);
                    accessConfigVO.setWebConfig(accessWebConfig);
                }
                if (accessConfigJsonNode.get(AccessConfigConstant.CONFIG_TYPE_API) != null) {
                    AccessApiConfig accessWxCpConfig = JSONUtil.toObject(accessConfigJsonNode.get(AccessConfigConstant.CONFIG_TYPE_API), AccessApiConfig.class);
                    accessConfigVO.setApiConfig(accessWxCpConfig);
                }
                return Result.success(accessConfigVO);
            }
        }
        return Result.success(accessConfigVO);
    }

    /**
     * 获取accessConfigWeb配置
     *
     * @param app
     * @return
     */
    public AccessWebConfig getAccessWebConfig(AiApp app) {
        // 尝试从app对象获取配置
        JsonNode accessNode = null;

        JsonNode appConfig = app.getConfig();
        if (appConfig != null) {
            accessNode = appConfig.get(AccessConfigConstant.ACCESS);
        } else {
            // 如果app配置为空，从数据库获取
            Result result = getConfigByCondition(
                    AppConfigConstant.LINK_TYPE_APP,
                    app.getId(),
                    AccessConfigConstant.ACCESS
            );

            if (result.isError()) {
                return null;
            }

            accessNode = (JsonNode) result.getData();
        }

        // 如果找不到访问配置，返回null
        if (accessNode == null) {
            return null;
        }

        // 返回web配置
        return JSONUtil.toObject(
                accessNode.get(AccessConfigConstant.CONFIG_TYPE_WEB),
                AccessWebConfig.class
        );
    }


    /**
     * 把appConfig放入缓存
     *
     * @param config
     */
    public void setCache(String linkType, Long linkId, JsonNode config) {
        CacheUtil.putObject(CacheUtil.KEY_APP_CONFIG + "_" + linkType + "_" + linkId, config, -1, null);
        logger.info("appConfig存入缓存:{}_{}_{},已放入缓存,无过期时间-----{}:", CacheUtil.KEY_APP_CONFIG, linkType, linkId, config);
    }
}
